package offer2;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantDemo {
    /*
    可重入锁：又名递归锁，是指同一个线程在外层方法获得锁的时候，再进入该线程的内层方法会自动获取锁（前提：锁对象是同一个对象），
    不会因为之前已经获取过还没释放而阻塞，也就是同一个线程可以多次获取同一把锁。

    synchronized和ReentrantLock就是可重入锁，可重入锁的一个优点就是可一定程度上的避免死锁
    */

    /*
    synchronized可重入锁的例子
    */
    public static final Object OBJECT = new Object();

    /*
    ReentrantLock的可重入锁的例子
    */
    static Lock lock = new ReentrantLock();

    public static class M1 implements Runnable {
        @Override
        public void run() {
            synchronized (OBJECT) {
                System.out.println("外层调用");
                synchronized (OBJECT) {
                    System.out.println("中层调用");
                    synchronized (OBJECT) {
                        System.out.println("内层调用");
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        M1 m1 = new M1();
        Thread thread = new Thread(m1);
        thread.start();

        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("外层锁");
                lock.lock();
                try {
                    System.out.println("内层锁");
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }).start();


        /*
        每个锁对象拥有一个锁计数器和一个指向持有该锁的线程的指针
        当执行monitorenter时，如果目标锁对象的计数器为零，那么说明它没有被其他线程所持有，java虚拟机会将该锁对象的持有线程设置为当前线程，
        并且将其计数器加一

        在目标锁对象的计数器不为零的情况下，如果锁对象的持有线程是当前线程，那么java虚拟机可以将其计数器加一，否则需要等待，直至持有线程释放该锁

        当执行monitorexit时，java虚拟机则需将锁对象的计数器减一，计数器为零则代表该锁已经被释放
        */
    }
}
